﻿using System;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Austin.HttpApi;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Deployment.Application;

namespace TorrentProxy
{
	public partial class MainForm : Form
	{
		private Proxy _proxy;
		private bool _isFirstRun;

		public MainForm()
		{
			InitializeComponent();
		}

		#region Form events
		private void notifyIcon_MouseDoubleClick(object sender, MouseEventArgs e)
		{
			RestoreFromMinimized();
		}

		private void MainForm_Resize(object sender, EventArgs e)
		{
			if (WindowState == FormWindowState.Minimized)
			{
				Mininize();
			}
		}

		private void MainForm_Shown(object sender, EventArgs e)
		{
#if !DEBUG
			if (Program.Settings.RunProxyOnLogon)
			{	
				StartProxy();
			}
#endif
		}

		private void MainForm_Load(object sender, EventArgs e)
		{
			Settings settings = Program.Settings;
			
			Text = String.Format(Text, Assembly.GetEntryAssembly().GetName().Version.ToString(3));
			txtPort.Text = settings.Port.ToString();
			chBoxRunAfterStart.Checked = settings.RunOnLogon;
			chBoxStartProxy.Checked = settings.RunProxyOnLogon;
			chBoxRunMinimized.Checked = settings.RunMinimized;

			switch(settings.LeechingMode)
			{
				case LeechingMode.Increase:
					rbIncreaseUploadedTraffic.Checked = true;
					break;
				case LeechingMode.Override:
					rbOverrideUploadedTraffic.Checked = true;
					break;
			}
			
			numMinOverrideUploaded.Value = Utils.RatioToPercents(settings.LeechingModeOverrideMinRatio);
			numMaxOverrideUploaded.Value = Utils.RatioToPercents(settings.LeechingModeOverrideMaxRatio);

			numMinIncreaseUploaded.Value = Utils.RatioToPercents(settings.LeechingModeIncreaseMinRatio);
			numMaxIncreaseUploaded.Value = Utils.RatioToPercents(settings.LeechingModeIncreaseMaxRatio);

			numMinOverrideUploadedWhenSeeding.Value = Utils.RatioToPercents(settings.SeedingModeMinRatio);
			numMaxOverrideUploadedWhenSeeding.Value = Utils.RatioToPercents(settings.SeedingModeMaxRatio);


			checkBoxChangeDownloadRatio.Checked = settings.DecreaseDownloadedTraffic;
			numDownloadRatio.Value = Utils.RatioToPercents(settings.DecreaseDownloadedTrafficRatio);

			_isFirstRun = !ApplicationDeployment.IsNetworkDeployed || ApplicationDeployment.CurrentDeployment == null ? true : ApplicationDeployment.CurrentDeployment.IsFirstRun;
			if (Program.Settings.RunMinimized && !_isFirstRun)
			{
				Mininize();
			}
		}

		private void btnStart_Click(object sender, EventArgs e)
		{
			StartProxy();
			if (_isFirstRun)
			{
				_isFirstRun = false;
				if (MessageBox.Show("Your μTorrent client should be configured correctly to work with TorrentProxy. Do you want to see how to configure μTorrent?", "TorrentProxy", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
				{
					ConfigureTorrent dialog = new ConfigureTorrent();
					dialog.ShowDialog(this);
				}
			}
		}

		private void btnStop_Click(object sender, EventArgs e)
		{
			StopProxy();
		}

		private void chBoxRunAfterStart_CheckedChanged(object sender, EventArgs e)
		{
			Program.Settings.RunOnLogon = chBoxRunAfterStart.Checked;
		}

		private void chBoxStartProxy_CheckedChanged(object sender, EventArgs e)
		{
			Program.Settings.RunProxyOnLogon = chBoxStartProxy.Checked;
		}

		private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
		{
			StopProxy();
			SaveSettings();
		}

		private void chBoxRunMinimized_CheckedChanged(object sender, EventArgs e)
		{
			Program.Settings.RunMinimized = chBoxRunMinimized.Checked;
		}

		private void showToolStripMenuItem_Click(object sender, EventArgs e)
		{
			RestoreFromMinimized();
		}

		private void exitToolStripMenuItem_Click(object sender, EventArgs e)
		{
			Application.Exit();
		}

		private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
		{
			ShowAbout();
		}

		private void howToConfigureUTorrentToolStripMenuItem_Click(object sender, EventArgs e)
		{
			ConfigureTorrent dialog = new ConfigureTorrent();
			dialog.ShowDialog(this);
		}

		private void reportABugToolStripMenuItem_Click(object sender, EventArgs e)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append(Uri.UriSchemeMailto + ":");
			stringBuilder.Append("mihail.merkulov@gmail.com");
			stringBuilder.Append("?subject=TorrentProxy");
			var info = new ProcessStartInfo(stringBuilder.ToString());
			Process.Start(info);
		}

		private void exitToolStripMenuItem1_Click(object sender, EventArgs e)
		{
			Application.Exit();
		}

		private void btnLog_Click(object sender, EventArgs e)
		{
			if (_proxy != null)
			{
				LogDialog dialog = new LogDialog { Proxy = _proxy };
				dialog.Show(this);
			}
		}
		#endregion

		#region Control form

		private void ShowAbout()
		{
			AboutBox about = new AboutBox();
			about.ShowDialog(this);
		}

		private void RestoreFromMinimized()
		{
			Show();
			WindowState = FormWindowState.Normal;
			ShowInTaskbar = true;
			ForceForeground();
		}

		private void Mininize()
		{
			ShowInTaskbar = false;
			WindowState = FormWindowState.Minimized;
			Hide();
		}

		private void ForceForeground()
		{
			if (!Visible)
			{
				Visible = true;
			}

			IntPtr handle = NativeMethods.GetForegroundWindow();

			if (handle == Handle)
			{
				return;
			}

			IntPtr processId = NativeMethods.GetWindowThreadProcessId(handle, IntPtr.Zero);
			IntPtr curProcessId = NativeMethods.GetWindowThreadProcessId(Handle, IntPtr.Zero);

			if (processId != curProcessId)
			{
				NativeMethods.AttachThreadInput(processId, curProcessId, true);
				NativeMethods.SetForegroundWindow(Handle);

				if (Application.OpenForms.Count > 1)
				{
					foreach (Form form in Application.OpenForms)
					{
						if (form.Modal)
						{
							NativeMethods.SetForegroundWindow(form.Handle);
							break;
						}
					}
				}

				NativeMethods.AttachThreadInput(processId, curProcessId, false);
			}
			else
			{
				NativeMethods.SetForegroundWindow(handle);
			}
		}		
		#endregion

		#region Proxy

		private void StartProxy()
		{
			btnStop.Enabled = true;
			btnStart.Enabled = false;
			txtPort.Enabled = false;

			try
			{
				uint port = uint.Parse(txtPort.Text);
				String url = String.Format("http://127.0.0.1:{0}/", port);
				UrlReservation reservation = UrlReservation.GetAll().FirstOrDefault(urlRes => urlRes.Url.Equals(url, StringComparison.InvariantCultureIgnoreCase));

				Boolean removeOldReservation = false;
				if (port != Program.Settings.Port)
				{
					String oldUrl = String.Format("http://127.0.0.1:{0}/", Program.Settings.Port);
					UrlReservation oldReservation = UrlReservation.GetAll().FirstOrDefault(urlRes => urlRes.Url.Equals(oldUrl, StringComparison.InvariantCultureIgnoreCase));
					removeOldReservation = true;
				}

				if (reservation == null || removeOldReservation)
				{
					String args = null;
					if (reservation == null)
					{
						args = String.Format("-reserve {0} ", port);
					}

					if (removeOldReservation)
					{
						args += String.Format("-removeReserve {0}", Program.Settings.Port);
					}

					if (RunElevated(Application.ExecutablePath, args, true) != 0)
					{
						throw new Exception("Cannot initialize Torrent Proxy");
					}
				}

				Boolean hasEveryone = reservation.SecurityIdentifiers.Any(sid => sid.IsWellKnown(System.Security.Principal.WellKnownSidType.WorldSid));
				if (!hasEveryone)
				{
					throw new Exception("Torrent Proxy cannot use this port. Please select another port.");
				}

				_proxy = new Proxy(port);
				_proxy.LeechingMode = rbIncreaseUploadedTraffic.Enabled ? LeechingMode.Increase : LeechingMode.Override;
				_proxy.LeechingModeOverrideMinRatio = Utils.PercentsToRatio((int)numMinOverrideUploaded.Value);
				_proxy.LeechingModeOverrideMaxRatio = Utils.PercentsToRatio((int)numMaxOverrideUploaded.Value);
				_proxy.LeechingModeIncreaseMinRatio = Utils.PercentsToRatio((int)numMinIncreaseUploaded.Value);
				_proxy.LeechingModeIncreaseMaxRatio = Utils.PercentsToRatio((int)numMaxIncreaseUploaded.Value);
				_proxy.SeedingModeMinRatio = Utils.PercentsToRatio((int)numMinOverrideUploadedWhenSeeding.Value);
				_proxy.SeedingModeMaxRatio = Utils.PercentsToRatio((int)numMaxOverrideUploadedWhenSeeding.Value);
				_proxy.DecreaseDownloadedTraffic = checkBoxChangeDownloadRatio.Checked;
				_proxy.DecreaseDownloadedTrafficRatio = Utils.PercentsToRatio((int) numDownloadRatio.Value);
				_proxy.Start();
			}
			catch (Exception ex)
			{
				MessageBox.Show(this, String.Format("Cannot start proxy: {0}", ex.Message), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
				btnStop.Enabled = false;
				btnStart.Enabled = true;
				txtPort.Enabled = true;
			}
		}

		private void StopProxy()
		{
			if (_proxy != null)
			{
				_proxy.Stop();
				_proxy.Dispose();
				_proxy = null;
			}

			btnStop.Enabled = false;
			btnStart.Enabled = true;
			txtPort.Enabled = true;
		}
		#endregion		

		#region Settings changed events
		private void checkBoxChangeDownloadRate_CheckedChanged(object sender, EventArgs e)
		{
			numDownloadRatio.Enabled = checkBoxChangeDownloadRatio.Checked;
			if (_proxy != null && _proxy.IsRunning)
			{
				_proxy.DecreaseDownloadedTraffic = checkBoxChangeDownloadRatio.Checked;
			}
		}

		private void rbOverrideUploadedTraffic_CheckedChanged(object sender, EventArgs e)
		{
			panelOverrideUploadedTraffic.Enabled = rbOverrideUploadedTraffic.Checked;
			panelIncreaseUploadedTraffic.Enabled = rbIncreaseUploadedTraffic.Checked;
			if (_proxy != null && _proxy.IsRunning)
			{
				_proxy.LeechingMode = rbOverrideUploadedTraffic.Checked ? LeechingMode.Override : LeechingMode.Increase;
			}
		}

		private void numMinOverrideUploaded_ValueChanged(object sender, EventArgs e)
		{
			if (_proxy != null && _proxy.IsRunning)
			{
				_proxy.LeechingModeOverrideMinRatio = Utils.PercentsToRatio((int)numMinOverrideUploaded.Value);
			}
		}

		private void numMaxOverrideUploaded_ValueChanged(object sender, EventArgs e)
		{
			if (_proxy != null && _proxy.IsRunning)
			{
				_proxy.LeechingModeOverrideMaxRatio = Utils.PercentsToRatio((int)numMaxOverrideUploaded.Value);
			}
		}

		private void numMinIncreaseUploaded_ValueChanged(object sender, EventArgs e)
		{
			if (_proxy != null && _proxy.IsRunning)
			{
				_proxy.LeechingModeIncreaseMinRatio = Utils.PercentsToRatio((int)numMinIncreaseUploaded.Value);
			}
		}

		private void numMaxIncreaseUploaded_ValueChanged(object sender, EventArgs e)
		{
			if (_proxy != null && _proxy.IsRunning)
			{
				_proxy.LeechingModeIncreaseMaxRatio = Utils.PercentsToRatio((int)numMaxIncreaseUploaded.Value);
			}
		}

		private void numMinOverrideUploadedWhenSeeding_ValueChanged(object sender, EventArgs e)
		{
			if (_proxy != null && _proxy.IsRunning)
			{
				_proxy.SeedingModeMinRatio = Utils.PercentsToRatio((int)numMinOverrideUploadedWhenSeeding.Value);
			}
		}

		private void numMaxOverrideUploadedWhenSeeding_ValueChanged(object sender, EventArgs e)
		{
			if (_proxy != null && _proxy.IsRunning)
			{
				_proxy.SeedingModeMaxRatio = Utils.PercentsToRatio((int)numMaxOverrideUploadedWhenSeeding.Value);
			}
		}

		private void numDownloadRatio_ValueChanged(object sender, EventArgs e)
		{
			if (_proxy != null && _proxy.IsRunning)
			{
				_proxy.DecreaseDownloadedTrafficRatio = Utils.PercentsToRatio((int)numDownloadRatio.Value);
			}
		} 
		#endregion

		#region Misc methods

		private void SaveSettings()
		{
			Settings settings = Program.Settings;

			settings.Port = Int32.Parse(txtPort.Text);
			settings.LeechingMode = rbIncreaseUploadedTraffic.Enabled ? LeechingMode.Increase : LeechingMode.Override;
			settings.LeechingModeOverrideMinRatio = Utils.PercentsToRatio((int)numMinOverrideUploaded.Value);
			settings.LeechingModeOverrideMaxRatio = Utils.PercentsToRatio((int)numMaxOverrideUploaded.Value);
			settings.LeechingModeIncreaseMinRatio = Utils.PercentsToRatio((int)numMinIncreaseUploaded.Value);
			settings.LeechingModeIncreaseMaxRatio = Utils.PercentsToRatio((int)numMaxIncreaseUploaded.Value);
			settings.SeedingModeMinRatio = Utils.PercentsToRatio((int)numMinOverrideUploadedWhenSeeding.Value);
			settings.SeedingModeMaxRatio = Utils.PercentsToRatio((int)numMaxOverrideUploadedWhenSeeding.Value);
			settings.DecreaseDownloadedTraffic = checkBoxChangeDownloadRatio.Checked;
			settings.DecreaseDownloadedTrafficRatio = Utils.PercentsToRatio((int)numDownloadRatio.Value);

			string startupLink = Environment.GetFolderPath(Environment.SpecialFolder.Startup);
			string executableName = Path.GetFileNameWithoutExtension(Application.ExecutablePath);
			startupLink = Path.Combine(startupLink, executableName) + ".appref-ms";

			if (Program.Settings.RunOnLogon)
			{
				if (!File.Exists(startupLink))
				{
					string link = Environment.GetFolderPath(Environment.SpecialFolder.Programs);
					link = Path.Combine(link, executableName, executableName + ".appref-ms");
					if (File.Exists(link))
					{
						File.Copy(link, startupLink);
					}
				}
			}
			else if (File.Exists(startupLink))
			{
				File.Delete(startupLink);
			}
		}

		/// <summary>
		/// Runs the elevated process.
		/// </summary>
		/// <param name="path">The application path.</param>
		/// <param name="arguments">The arguments.</param>
		/// <param name="waitForExit">if set to <c>true</c> the method will wait the application exit event.</param>
		/// <returns></returns>
		/// <remarks>Works on Vista and newer OS's</remarks>
		public static int RunElevated(String path, String arguments, Boolean waitForExit)
		{
			try
			{
				Process p = new Process { StartInfo = { FileName = path, Verb = "runas", Arguments = arguments } };

				if (!p.Start())
				{
					return -1;
				}

				if (waitForExit)
				{
					p.WaitForExit();
					return p.ExitCode;
				}

				return 0;
			}
			catch (Win32Exception ex)
			{
				throw new Exception("Cannot run elevated process: " + path, ex);
			}
		}
		#endregion

		#region Auto settings
		private void btnAutoSettingsKeepRatio_Click(object sender, EventArgs e)
		{
			rbOverrideUploadedTraffic.Checked = true;
			numMinOverrideUploaded.Value = 90;
			numMaxOverrideUploaded.Value = 110;
			numMinOverrideUploadedWhenSeeding.Value = 100;
			numMaxOverrideUploadedWhenSeeding.Value = 100;
			checkBoxChangeDownloadRatio.Checked = false;
		} 		

		private void btnAutoSettingsLowIncrease_Click(object sender, EventArgs e)
		{
			rbIncreaseUploadedTraffic.Checked = true;
			numMinIncreaseUploaded.Value = 100;
			numMaxIncreaseUploaded.Value = 120;
			numMinOverrideUploadedWhenSeeding.Value = 100;
			numMaxOverrideUploadedWhenSeeding.Value = 120;
			checkBoxChangeDownloadRatio.Checked = false;
		}

		private void btnAutoSettingsMediumIncrease_Click(object sender, EventArgs e)
		{
			rbIncreaseUploadedTraffic.Checked = true;
			numMinIncreaseUploaded.Value = 120;
			numMaxIncreaseUploaded.Value = 150;
			numMinOverrideUploadedWhenSeeding.Value = 120;
			numMaxOverrideUploadedWhenSeeding.Value = 150;
			checkBoxChangeDownloadRatio.Checked = false;
		}

		private void btnAutoSettingsHighIncrease_Click(object sender, EventArgs e)
		{
			rbIncreaseUploadedTraffic.Checked = true;
			numMinIncreaseUploaded.Value = 150;
			numMaxIncreaseUploaded.Value = 180;
			numMinOverrideUploadedWhenSeeding.Value = 150;
			numMaxOverrideUploadedWhenSeeding.Value = 180;
			checkBoxChangeDownloadRatio.Checked = false;
		}

		#endregion
	}
}
